﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using System.Xml.Serialization;
using System.IO;
using System.Diagnostics;
using CRMAdminDashboard.CrmSvc;

namespace CRMAdminDashboard
{
    /// <summary>
    /// Code behind FormCompareRoles
    /// </summary>
    public class HelperCompareRoles : HelperBase
    {

        #region Constants

        private const string RES_ROLESFORM_CREATEEQUIV = "RES_ROLESFORM_CREATEEQUIV";
        private const string RES_ROLESFORM_CREATEROLE_TITLE = "RES_ROLESFORM_CREATEROLE_TITLE";
        private const string RES_ROLESFORM_SUBTRACT1 = "RES_ROLESFORM_SUBTRACT1";
        private const string RES_ROLESFORM_SUBTRACT2 = "RES_ROLESFORM_SUBTRACT2";
        private const string RES_ROLESFORM_SUBTRACT3 = "RES_ROLESFORM_SUBTRACT3";
        private const string RES_ROLESFORM_SELECTBYUSER = "RES_ROLESFORM_SELECTBYUSER";

        #endregion

        #region Fields

        /// <summary>
        /// The CRM Service handle
        /// </summary>
        private CachingCrmService _svcHandle;

        /// <summary>
        /// Relationship between roles
        /// </summary>
        private RoleUserHierarchy _rUHierarchy;

        /// <summary>
        /// All system privileges (keyed by Guid)
        /// </summary>
        private Dictionary<Guid, SystemPrivilege> _allPrivilegesByGuid;

        /// <summary>
        /// All system privileges (keyed by Name)
        /// </summary>
        private Dictionary<string, SystemPrivilege> _allPrivilegesByName;

        #endregion

        #region Properties

        /// <summary>
        /// BuFilter is persisted in RoleUserHierarchy
        /// </summary>
        public Dictionary<Guid, BusinessUnit> BuFilter
        {
            get
            {
                return RUHierarchy.BuFilter;
            }
        }

        /// <summary>
        /// All system privileges (keyed by Guid)
        /// </summary>
        public Dictionary<Guid, SystemPrivilege> AllPrivilegesByGuid
        {
            get
            {
                if (_allPrivilegesByGuid == null)
                {
                    GetAllPrivileges();
                }
                return _allPrivilegesByGuid;
            }
        }

        /// <summary>
        /// All system privileges (keyed by Name)
        /// </summary>
        public Dictionary<string, SystemPrivilege> AllPrivilegesByName
        {
            get
            {
                if (_allPrivilegesByName == null)
                {
                    GetAllPrivileges();
                }
                return _allPrivilegesByName;
            }
        }

        /// <summary>
        /// Relationship between roles
        /// </summary>
        public RoleUserHierarchy RUHierarchy
        {
            get
            {
                if (_rUHierarchy == null)
                {
                    _rUHierarchy = new RoleUserHierarchy(_svcHandle);
                }
                return _rUHierarchy;
            }
        }

        #endregion

        #region Entry Points

        /// <summary>
        /// Standard ctor.
        /// </summary>
        public HelperCompareRoles(CachingCrmService svcHandle)
        {
            _svcHandle = svcHandle;
        }

        /// <summary>
        /// Get all roles and populate the list boxes
        /// </summary>
        public void GetRoles(ListBox lstLeft, ListBox lstRight)
        {
            lstLeft.SelectedIndex = -1;
            lstLeft.Items.Clear();
            lstRight.SelectedIndex = -1;
            lstRight.Items.Clear();
            this.RUHierarchy.Clear();
            this._svcHandle.InvalidateCache();
            _allPrivilegesByGuid = null;
            _allPrivilegesByName = null;

            try
            {
                // get the BUs
                FormSelectBUs dialog = new FormSelectBUs(_svcHandle);
                dialog.BuFilter = RUHierarchy.BuFilter;
                if (dialog.ShowDialog() == DialogResult.OK)
                {
                    this.RUHierarchy.BuFilter = dialog.BuFilter;

                    foreach (SystemRole rl in this.RUHierarchy.FilteredRoles.Values)
                    {
                        lstLeft.Items.Add(rl);
                        lstRight.Items.Add(rl);
                    }
                }
            }
            catch (Exception ex)
            {
                FormCompareRoles.WriteLog(true, ex);
            }
        }

        /// <summary>
        /// Save all roles as XML file
        /// </summary>
        public void DumpAllRoles()
        {
            try
            {
                // get roles with *all* their columns
                Dictionary<Guid, SystemRole> allRoles = SystemRole.GetAllRoles(_svcHandle, AllPrivilegesByGuid, new AllColumns());

                // make the roles serializable
                List<SerializableRole> allSerializableRoles = new List<SerializableRole>();
                foreach (SystemRole sr in allRoles.Values)
                {
                    if (RUHierarchy.FilteredRoles.ContainsKey(sr.Id))
                    {
                        SerializableRole role = new SerializableRole(_svcHandle, sr, RUHierarchy, AllPrivilegesByGuid);
                        allSerializableRoles.Add(role);
                    }
                }

                XmlSerializer serializer = new XmlSerializer(typeof(List<SerializableRole>));
                string fileName;
                this.SaveXmlFile(serializer, allSerializableRoles, out fileName);
                FormCompareRoles.WriteLog(true, string.Format("File [{0}] saved successfully." + Environment.NewLine, fileName));
            }
            catch (Exception ex)
            {
                FormCompareRoles.WriteLog(true, ex);
            }
        }

        /// <summary>
        /// Compare two sets of roles
        /// </summary>
        public void CompareRoles(IEnumerable<SystemRole> leftList, IEnumerable<SystemRole> rightList, int mode)
        {
            try
            {
                RoleDiffCollection differences = SystemRole.Compare(_svcHandle, AllPrivilegesByGuid, leftList, rightList);
                switch (mode)
                {
                    case 0:
                        // TXT comparison
                        CompareOutputTxt(differences);
                        break;
                    case 1:
                        // CSV comparison
                        CompareOutputCsv(differences);
                        break;
                    case 2:
                        // XML comparison
                        CompareOutputXml(differences);
                        break;
                }
            }
            catch (Exception ex)
            {
                FormCompareRoles.WriteLog(true, ex);
            }
        }

        /// <summary>
        /// Show the privileges of one merged set of roles
        /// </summary>
        public void ShowPrivileges(IEnumerable<SystemRole> list, int mode)
        {
            try
            {
                RoleDiffCollection merged = SystemRole.Compare(_svcHandle, AllPrivilegesByGuid, list);
                switch (mode)
                {
                    case 0:
                        // TXT
                        CompareOutputTxt(merged);
                        break;
                    case 1:
                        // CSV
                        CompareOutputCsv(merged);
                        break;
                    case 2:
                        // XML
                        CompareOutputXml(merged);
                        break;
                }
            }
            catch (Exception ex)
            {
                FormCompareRoles.WriteLog(true, ex);
            }
        }

        /// <summary>
        /// Select roles based on a user
        /// </summary>
        public void SelectByUser(ListBox lstBox)
        {
            FormSelectByUser dialog = new FormSelectByUser(Program.GetResString(RES_ROLESFORM_SELECTBYUSER), RUHierarchy);

            if (dialog.ShowDialog() == DialogResult.OK)
            {
                // select their roles in the list box
                SystemUser user = dialog.SelectedUser;
                Dictionary<Guid, SystemRole> userRoles = user.GetRoles(_svcHandle, SystemRole.SystemRoleColumnSet);

                lstBox.ClearSelected();
                for (int idx = 0; idx < lstBox.Items.Count; idx++)
                {
                    SystemRole listBoxRole = (SystemRole)lstBox.Items[idx];
                    foreach (SystemRole userRole in userRoles.Values)
                    {
                        // we select the roles that contain the derived role the user has
                        if (listBoxRole.DerivedRoles.ContainsKey(userRole.Id))
                        {
                            lstBox.SetSelected(idx, true);
                            break;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Create role equivalent to one merged set of roles
        /// </summary>
        public bool CreateEquivalent(ListBox listBox)
        {
            try
            {
                string newRoleName;
                BusinessUnit newRoleBU;
                if (CreateRoleDialog(out newRoleBU, out newRoleName))
                {
                    // confirm
                    string str1 = Program.GetResString(RES_ROLESFORM_CREATEEQUIV);
                    string str2 = Environment.NewLine + Environment.NewLine;
                    foreach (SystemRole sr in listBox.SelectedItems)
                    {
                        str2 += sr.ToString() + Environment.NewLine;
                    }
                    str2 += Environment.NewLine;
                    string str = string.Format(str1, string.Format("\r\n\r\n[{0}] {1}\r\n\r\n", newRoleBU.Name, newRoleName), str2);

                    if (MessageBox.Show(str, Program.GetResString(RES_ROLESFORM_CREATEROLE_TITLE),
                        MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                    {
                        SystemRole.CreateEquivalentRole(_svcHandle, newRoleName, newRoleBU,
                            AllPrivilegesByName, AllPrivilegesByGuid, new ArrayList(listBox.SelectedItems).Cast<SystemRole>());
                        FormCompareRoles.WriteLog(true, string.Format("Role [{0}] {1} created successfully." + Environment.NewLine, newRoleBU.Name, newRoleName));

                        // we need to recalculate this
                        RUHierarchy.Clear();
                        return true;
                    }
                }
            }
            catch (Exception ex)
            {
                FormCompareRoles.WriteLog(true, ex);
            }

            return false;
        }

        /// <summary>
        /// Create a role equivalent to the difference between two merged sets of roles
        /// </summary>
        public bool SubtractRoles(ListBox lstLeft, ListBox lstRight)
        {
            try
            {
                string newRoleName;
                BusinessUnit newRoleBU;
                if (CreateRoleDialog(out newRoleBU, out newRoleName))
                {
                    // confirm
                    string str = Program.GetResString(RES_ROLESFORM_SUBTRACT1) + Environment.NewLine + Environment.NewLine;
                    str += "New Role (C):" + Environment.NewLine + Environment.NewLine +
                        "[" + newRoleBU.Name + "] " + newRoleName + Environment.NewLine + Environment.NewLine +
                        "Collection A:" + Environment.NewLine + Environment.NewLine;
                    foreach (SystemRole sr in lstLeft.SelectedItems)
                    {
                        str += sr.ToString() + Environment.NewLine;
                    }
                    str += Environment.NewLine + "Collection B:" + Environment.NewLine + Environment.NewLine;
                    foreach (SystemRole sr in lstRight.SelectedItems)
                    {
                        str += sr.ToString() + Environment.NewLine;
                    }
                    str += Environment.NewLine + Program.GetResString(RES_ROLESFORM_SUBTRACT2) + Environment.NewLine;
                    str += Program.GetResString(RES_ROLESFORM_SUBTRACT3) + Environment.NewLine;
                    str += Environment.NewLine + "Proceed?";

                    if (MessageBox.Show(str, Program.GetResString(RES_ROLESFORM_CREATEROLE_TITLE), MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                    {
                        SystemRole.CreateSubtractionRole(_svcHandle, newRoleName, newRoleBU,
                            AllPrivilegesByName, AllPrivilegesByGuid,
                            new ArrayList(lstLeft.SelectedItems).Cast<SystemRole>(),
                            new ArrayList(lstRight.SelectedItems).Cast<SystemRole>());
                        FormCompareRoles.WriteLog(true, string.Format("Role [{0}] {1} created successfully." + Environment.NewLine, newRoleBU.Name, newRoleName));

                        // we need to refresh the roles
                        RUHierarchy.Clear();
                        return true;
                    }
                }
            }
            catch (Exception ex)
            {
                FormCompareRoles.WriteLog(true, ex);
            }

            return false;
        }

        #endregion

        #region Local Methods

        /// <summary>
        /// Get all system privileges
        /// </summary>
        private void GetAllPrivileges()
        {
            RetrievePrivilegeSetRequest request = new RetrievePrivilegeSetRequest();
            RetrievePrivilegeSetResponse response = (RetrievePrivilegeSetResponse)_svcHandle.Execute(request);

            Dictionary<Guid, SystemPrivilege> htGuid = new Dictionary<Guid, SystemPrivilege>();
            Dictionary<string, SystemPrivilege> htName = new Dictionary<string, SystemPrivilege>();
            foreach (BusinessEntity be in response.BusinessEntityCollection.BusinessEntities)
            {
                SystemPrivilege spv = new SystemPrivilege((privilege)be);
                htGuid.Add(spv.Id, spv);
                htName.Add(spv.PrivilegeName, spv);
            }

            _allPrivilegesByGuid = htGuid;
            _allPrivilegesByName = htName;
        }

        /// <summary>
        /// Output the role differences as TXT. This method is used both to print differences and to print only
        /// one set.
        /// </summary>
        private void CompareOutputTxt(RoleDiffCollection differences)
        {
            FormCompareRoles.ClearLog();
            if (differences.RightRoles != null)
            {
                FormCompareRoles.WriteLog(false, "Comparing:" + Environment.NewLine);
            }
            else
            {
                FormCompareRoles.WriteLog(false, "Roles:" + Environment.NewLine);
            }

            // first output the actual role names
            bool first = true;
            foreach (string role in differences.LeftRoles)
            {
                if (first)
                {
                    FormCompareRoles.WriteLog(false, "      LEFT : ");
                    first = false;
                }
                else
                {
                    FormCompareRoles.WriteLog(false, "    + LEFT : ");
                }
                FormCompareRoles.WriteLog(false, role + Environment.NewLine);
            }

            if (differences.RightRoles != null)
            {
                FormCompareRoles.WriteLog(false, "With:" + Environment.NewLine);
                first = true;
                foreach (string role in differences.RightRoles)
                {
                    if (first)
                    {
                        FormCompareRoles.WriteLog(false, "      RIGHT: ");
                        first = false;
                    }
                    else
                    {
                        FormCompareRoles.WriteLog(false, "    + RIGHT: ");
                    }
                    FormCompareRoles.WriteLog(false, role + Environment.NewLine);
                }
            }

            if (differences.RightRoles != null)
            {
                FormCompareRoles.WriteLog(false, Environment.NewLine + "Differences:" + Environment.NewLine + Environment.NewLine);
                FormCompareRoles.WriteLog(false, "Left Right Privilege" + Environment.NewLine);
                FormCompareRoles.WriteLog(false, "---- ----- ---------------------------------------------" + Environment.NewLine);
            }
            else
            {
                FormCompareRoles.WriteLog(false, "Depth                        Privilege" + Environment.NewLine);
                FormCompareRoles.WriteLog(false, "---------------------------- ---------------------------------------------" + Environment.NewLine);
            }

            int diffDepth = 0;
            int leftOnly = 0;
            int rightOnly = 0;

            // output differences in depth
            List<string> rows1 = new List<string>();
            foreach (RoleDiff difference in differences.RoleDifferences)
            {
                if (difference.LeftPrivilegeDepth != null && difference.RightPrivilegeDepth != null)
                {
                    diffDepth++;
                    rows1.Add(difference.ToStringTxt());
                }
            }
            rows1.Sort(RoleDiff.CompareToStringTxt);

            // output privileges on left or right only
            List<string> rows2 = new List<string>();
            foreach (RoleDiff difference in differences.RoleDifferences)
            {
                if (difference.LeftPrivilegeDepth != null && difference.RightPrivilegeDepth != null)
                {
                    continue;
                }
                else if (difference.LeftPrivilegeDepth != null)
                {
                    leftOnly++;
                }
                else
                {
                    rightOnly++;
                }

                if (differences.RightRoles != null)
                {
                    rows2.Add(difference.ToStringTxt());
                }
                else
                {
                    rows2.Add(difference.ToStringOneSetTxt());
                }
            }
            if (differences.RightRoles != null)
            {
                rows2.Sort(RoleDiff.CompareToStringTxt);
            }
            else
            {
                rows2.Sort(RoleDiff.CompareToStringOneSetTxt);
            }

            foreach (string difference in rows1.Concat(rows2))
            {
                FormCompareRoles.WriteLog(false, difference + Environment.NewLine);
            }

            if (differences.RightRoles != null)
            {
                FormCompareRoles.WriteLog(false, "---- ----- -------------------------" + Environment.NewLine);
                FormCompareRoles.WriteLog(false, "Differences in depth: " + diffDepth + Environment.NewLine);
                FormCompareRoles.WriteLog(false, "    In left set only: " + leftOnly + Environment.NewLine);
                FormCompareRoles.WriteLog(false, "   In right set only: " + rightOnly + Environment.NewLine);
            }
            else
            {
                FormCompareRoles.WriteLog(false, "---------------------------- ---------------------------------------------" + Environment.NewLine);
                FormCompareRoles.WriteLog(false, "Privileges: " + leftOnly + Environment.NewLine);
            }

            FormCompareRoles.WriteLog(false, "------------------------------------" + Environment.NewLine);
            FormCompareRoles.WriteLog(false, "Key:  1U: User (Basic)" + Environment.NewLine);
            FormCompareRoles.WriteLog(false, "      2B: Business Unit (Local)" + Environment.NewLine);
            FormCompareRoles.WriteLog(false, "      3P: Parent: Child Business Units (Deep)" + Environment.NewLine);
            FormCompareRoles.WriteLog(false, "      4O: Organization (Global)" + Environment.NewLine);
        }

        /// <summary>
        /// Output the role differences as CSV. This method is used both to print differences and to print only
        /// one set.
        /// </summary>
        private void CompareOutputCsv(RoleDiffCollection differences)
        {
            FormCompareRoles.ClearLog();
            StringBuilder sb = new StringBuilder();

            if (differences.RightRoles != null)
            {
                sb.Append("Left Privilege,Right Privilege,Privilege Name,Privilege Name Long" + Environment.NewLine);
            }
            else
            {
                sb.Append("Privilege,Privilege Name,Privilege Name Long" + Environment.NewLine);
            }

            FormCompareRoles.WriteLog(false, sb.ToString());

            int diffDepth = 0;
            int leftOnly = 0;
            int rightOnly = 0;

            // output differences in depth
            List<string> rows1 = new List<string>();
            foreach (RoleDiff difference in differences.RoleDifferences)
            {
                if (difference.LeftPrivilegeDepth != null && difference.RightPrivilegeDepth != null)
                {
                    diffDepth++;
                    rows1.Add(difference.ToStringCsv());
                }
            }
            rows1.Sort(RoleDiff.CompareToStringCsv);

            // output privileges on left or right only
            List<string> rows2 = new List<string>();
            foreach (RoleDiff difference in differences.RoleDifferences)
            {
                if (difference.LeftPrivilegeDepth != null && difference.RightPrivilegeDepth != null)
                {
                    continue;
                }
                else if (difference.LeftPrivilegeDepth != null)
                {
                    leftOnly++;
                }
                else
                {
                    rightOnly++;
                }

                if (differences.RightRoles != null)
                {
                    rows2.Add(difference.ToStringCsv());
                }
                else
                {
                    rows2.Add(difference.ToStringOneSetCsv());
                }
            }
            if (differences.RightRoles != null)
            {
                rows2.Sort(RoleDiff.CompareToStringCsv);
            }
            else
            {
                rows2.Sort(RoleDiff.CompareToStringOneSetCsv);
            }

            foreach (string difference in rows1.Concat(rows2))
            {
                sb.Append(difference + Environment.NewLine);
                FormCompareRoles.WriteLog(false, difference + Environment.NewLine);
            }

            SaveFileDialog dialog = new SaveFileDialog();
            dialog.Filter = "CSV files (*.csv)|*.csv|All files (*.*)|*.*";
            dialog.OverwritePrompt = true;

            if (dialog.ShowDialog() == DialogResult.OK)
            {
                StreamWriter file = new StreamWriter(dialog.FileName, false);
                file.Write(sb.ToString());
                file.Close();
            }

        }

        /// <summary>
        /// Output the role differences as XML
        /// </summary>
        private void CompareOutputXml(RoleDiffCollection differences)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(RoleDiffCollection));

            // show the differences
            FormCompareRoles.ClearLog();
            FormCompareRoles.WriteLog(false, IndentXml(serializer, differences) + Environment.NewLine);

            // save them as a XML file
            string fileName;
            SaveXmlFile(serializer, differences, out fileName);
            FormCompareRoles.WriteLog(true, string.Format("File [{0}] saved successfully." + Environment.NewLine, fileName));
        }

        /// <summary>
        /// Display the CreateRole dialog
        /// </summary>
        /// <returns>true if successful, false is user canceled</returns>
        private bool CreateRoleDialog(out BusinessUnit roleBU, out string roleName)
        {
            FormCreateRole dialog = new FormCreateRole(RUHierarchy, _svcHandle);

            if (dialog.ShowDialog() == DialogResult.OK)
            {
                roleBU = dialog.NewRoleBU;
                roleName = dialog.NewRoleName;
                return true;
            }
            else
            {
                roleBU = null;
                roleName = null;
                return false;
            }
        }

        #endregion

    }
}
